﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;

using JigLibSDX;
using JigLibSDX.Collision;
using JigLibSDX.Geometry;
using JigLibSDX.Math;
using JigLibSDX.Physics;
using JigLibSDX.Utils;

using SDX = SlimDX;
using SD9 = SlimDX.Direct3D9;

using DemoFramework;

namespace Demo_0
{
    public partial class Demo : Form
    {
        #region Non-Physics Code: Attributes
        //directx
        private SD9.Device _graphigsDevice;
        private SD9.Direct3D _direct3D;

        //screen size
        private const int _WIDTH = 640;
        private const int _HEIGHT = 480;

        //cam
        private Camera _camera;

        //input
        private BasicInput _input;

        //timing
        private Stopwatch _stopwatch;
        private float _secondsPerFrame = 0.016666f;
        #endregion

        private PhysicsSystem _physics;

        #region Non-Physics Code: Basic Init, Update & Terminate Handling
        #region Init
        public Demo()
        {
            InitializeComponent();
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
            this.Width = _WIDTH;
            this.Height = _HEIGHT;

            _camera = new Camera(new SDX.Vector3(0f, 2f, -10f), SDX.Vector3.Zero, SDX.Vector3.UnitY, _WIDTH, _HEIGHT, (float)Math.PI / 2f, 0.001f, 100f);

            InitializeGraphics();
            InitializeLight();
            InitializePhysics();

            LoadContent();

            _input = new BasicInput(this.Handle);
            _stopwatch = new Stopwatch();
        }
        #endregion

        #region Form Closing
        private void Demo_FormClosing(object sender, FormClosingEventArgs e)
        {
            UnloadContent();
        }
        #endregion

        #region Update & Draw Controller
        private void Demo_Paint(object sender, PaintEventArgs e)
        {
            if (_stopwatch.IsRunning)
            {
                float elapsedSeconds = _stopwatch.ElapsedMilliseconds * 0.001f;

                if (elapsedSeconds >= _secondsPerFrame)
                {
                    _stopwatch.Reset();
                    _stopwatch.Start();

                    Update(elapsedSeconds);
                    Draw(elapsedSeconds);
                }
            }
            else
            {
                _stopwatch.Start();
            }

            this.Invalidate();
        }
        #endregion

        #region Init Graphics
        private void InitializeGraphics()
        {
            SD9.PresentParameters presentParams = new SD9.PresentParameters();
            presentParams.BackBufferWidth = _WIDTH;
            presentParams.BackBufferHeight = _HEIGHT;
            presentParams.BackBufferFormat = SD9.Format.A8R8G8B8;
            presentParams.DeviceWindowHandle = this.Handle;

            _direct3D = new SD9.Direct3D();
            _graphigsDevice = new SD9.Device(_direct3D, 0, SD9.DeviceType.Hardware, this.Handle, SD9.CreateFlags.HardwareVertexProcessing, presentParams);

            _graphigsDevice.SetRenderState(SD9.RenderState.AlphaBlendEnable, true);
            _graphigsDevice.SetRenderState(SD9.RenderState.SourceBlend, SD9.Blend.SourceAlpha);
            _graphigsDevice.SetRenderState(SD9.RenderState.DestinationBlend, SD9.Blend.BothInverseSourceAlpha);
            _graphigsDevice.SetRenderState(SD9.RenderState.Lighting, true);
            _graphigsDevice.SetRenderState(SD9.RenderState.CullMode, SD9.Cull.None);
            _graphigsDevice.SetRenderState(SD9.RenderState.FillMode, SD9.FillMode.Solid);
            _graphigsDevice.SetRenderState(SD9.RenderState.NormalizeNormals, true);
        }
        #endregion

        #region Init Lights
        private void InitializeLight()
        {
            //light 1
            SDX.Vector3 direction = new SDX.Vector3(-1f, 0f, -1f);
            direction.Normalize();

            SD9.Light light = new SD9.Light();
            light.Type = SD9.LightType.Directional;
            light.Direction = direction;
            light.Diffuse = Color.White;
            light.Ambient = Color.White;

            _graphigsDevice.SetLight(0, light);
            _graphigsDevice.EnableLight(0, true);

            //light 2
            direction = new SDX.Vector3(1f, 0f, 1f);
            direction.Normalize();

            light = new SD9.Light();
            light.Type = SD9.LightType.Directional;
            light.Direction = direction;
            light.Diffuse = Color.White;
            light.Ambient = Color.White;

            _graphigsDevice.SetLight(1, light);
            _graphigsDevice.EnableLight(1, true);
        }
        #endregion
        #endregion

        private void InitializePhysics()
        {
            _physics = new PhysicsSystem();

            _physics.CollisionSystem = new CollisionSystemSAP();
            _physics.CollisionSystem.UseSweepTests = true;

            /// I'm using PhysicsSystem.Solver.Accumulated because it's the best looking solver.
            /// This might be slow if you tend to have worlds with many complex objects.
            _physics.SolverType = PhysicsSystem.Solver.Accumulated;

            /// Improves speed and prevents jiggling.
            _physics.EnableFreezing = true;

            _physics.NumCollisionIterations = 10;
            _physics.NumContactIterations = 10;
            _physics.NumPenetrationRelaxtionTimesteps = 15;
        }

        #region Non-Physics Code: Load & Unload Content
        private void LoadContent()
        {
            
        }

        private void UnloadContent()
        {

        }
        #endregion

        private void Update(float elapsedSeconds)
        {
            #region Non-Physics Code: Input Update
            _input.Update();
            #endregion

            //Apply forces
            // ...

            //Update Physics
            _physics.Update(elapsedSeconds);

            #region Non-Physics: Note
            //Update Positions
            // ... for example: Camera or graphics positions that depend on objects from the physics world.
            #endregion
        }

        #region Non-Physics Code: Draw
        private void Draw(float elapsedSeconds)
        {
            _graphigsDevice.Clear(SD9.ClearFlags.Target | SD9.ClearFlags.ZBuffer, Color.CornflowerBlue, 1.0f, 0);
            _graphigsDevice.BeginScene();


            _graphigsDevice.EndScene();
            _graphigsDevice.Present();
        }
        #endregion
    }
}
